﻿(function() {
	/**
	 * This is the callback function when the container
	 * sends us the next events.
	 */
	function eventsCallback(events) {
		storedEvents=events;
		drawScreen();
	}

	/**
	 * Ask the container for the next calendar event.
	 */
	function getNextEvents() {
		// Get the next events for the current user.
		// Retrieve events for the next two weeks to
		// find it.
		var start = new Date();
		var end = new Date();
		end.setDate(end.getDate() + 14);
		google.calendar.read.getEvents(
				eventsCallback, 'selected',
				google.calendar.utils.fromDate(start, timeOffset),
				google.calendar.utils.fromDate(end, timeOffset),
				{'requestedFields': ['status']});
	}

	/**
	 * Get the translated message for the ID.
	 * @param {string} id The id of the message.
	 * @return {string} The resulting string.
	 */
	var getMsg = (function(_prefs) {
		return function(id) {
			if(!_prefs)
				_prefs = new gadgets.Prefs();
			return _prefs.getMsg(id);
		}
	}(null));

	/** Fill a DOM node with a time generated from an array
	 *	@param {DOM node} n The node to fill
	 *	@param {Array<unitTime>} e The array of time elements
	 */
	function fillTimeNode(n, e) {
		var span;
		for(var i=0; i < e.length; i++) {
			span=document.createElement('span');
			span.appendChild(document.createTextNode(e[i].time));
			span.className = 'timenumber';
			n.appendChild(span);
			n.appendChild(document.createTextNode(' ' +
					getMsg(e[i].unit +
					(e[i].time === 1 ? 'Singular' : 'Plural')) +
					(i + 1 < e.length ? ', ' : '')));
		}
		return n;
	}

	function unitTime(time, unit) {
		this.time=time;
		this.unit=unit;
	}

	/**
	 * Given a Calendar API style time and a DOM node, fill
	 * the node the length of time until that time occurs.
	 * @param {Object} t The calendar API style time.
	 * @param {DOM node} n The node to fill
	 * TODO - fix this method of simpler i18n + pluralization.
	 */
	function fillTimeToNow(t, n) {
		var now = google.calendar.utils.getNow(timeOffset);
		var event_start = google.calendar.utils.toDate(t);
		var diff = (event_start.getTime() - now.getTime()) /
				(60 * 1000);
		diff = Math.round(diff);
		var minutes = diff % 60;
		diff = Math.floor(diff / 60);
		if(diff === 0)
			return fillTimeNode(n,
					[new unitTime(minutes, 'Minute')]);
		var hours = diff % 24;
		diff = Math.floor(diff / 24);
		if(diff === 0)
			return fillTimeNode(n,
					[new unitTime(hours, 'Hour'),
					new unitTime(minutes, 'Minute')]);
		var days = diff % 7;
		diff = Math.floor(diff / 7);
		if(diff === 0)
			return fillTimeNode(n,
					[new unitTime(days, 'Day'),
					hours ? new unitTime(hours, 'Hour'):
					        new unitTime(minutes, 'Minute')]);
		return fillTimeNode(n,
				[new unitTime(diff * 7 + days, 'Day')]);
	}

	/**
	 * The ID of the event onscreen, if any.
	 * @type {string?}
	 */
	var curEventId = null;

	/**
	 * The offset from the current system time to the
	 * calendar displayed time.
	 */
	var timeOffset = 0;

	/**
	 * An array of events.
	 * @type {Array<Object>}
	 */
	var storedEvents = null;

	/**
	 * The number of times that we've refreshed the events.
	 * @type {number}
	 */
	var refreshEvents = 0;

	/**
	 * Draw the gadget onscreen now, and every minute
	 * in the future.
	 */
	function redrawPeriodically() {
		drawScreen();
		var local_now = new Date();
		var s = local_now.getSeconds();
		var ms_until_next_min = (60 - s) * 1000;
		// Refresh events every 10 minutes
		if (refreshEvents++ % 10 === 9)
			getNextEvents();
		window.setTimeout(redrawPeriodically, ms_until_next_min);
	}

	/**
	 * Cross-browser compatible function to add an event
	 * listener to an object
	 * @param {DOM node} node The element
	 * @param {string} event The event type
	 * @param {function} func The function for the event listner
	 */
	function addListener(node, event, func) {
		if(node.addEventListener)
			node.addEventListener(event, func, true);
		else
			node.attachEvent("on" + event, func);
	}

	/**
	 * Create a function to change the color of an object's
	 * children
	 * @param {DOM node} node The object
	 * @param {string} color The color to change
	 * @return {function} The function that will effect
	 *                    this change
	 */
	function colorChangeFunc(node, color) {
		function f() {
			for(var i=0; i < node.childNodes.length; i++)
				node.childNodes[i].style.backgroundColor = color;
		}
		return f;
	}

	/**
	 * Draw the next event on the screen.
	 */
	function drawScreen() {
		var toDate=google.calendar.utils.toDate;
		var main = document.getElementById('main');
		var now = google.calendar.utils.getNow(timeOffset).getTime();
		var count = (storedEvents && storedEvents.length) || 0;
		var soonest;
		for (var i = 0; i < count; ++i)
			for (var j = 0; storedEvents[i] &&
					j < storedEvents[i].events.length; j++) {
				var e = storedEvents[i].events[j];
				if (!e.allDay &&
						e.status !== 'declined' &&
						toDate(e.startTime).getTime() >= now) {
					if (!soonest ||
							toDate(e.startTime).getTime() <
							toDate(soonest.startTime).getTime())
						soonest = e;
					break;
				}
			}
		var t;
		while(main.childNodes.length > 0)
			main.removeChild(main.firstChild);
		if (soonest) {
			var chipColor, hoverColor, borderColor;
			if (soonest.palette) {
				chipColor = soonest.palette.medium;
				hoverColor = soonest.palette.light;
				borderColor = soonest.palette.dark;
			} else {
				chipColor = '#999'; // Generic non-color
				hoverColor = '#bbb';
				borderColor = '#555';
			}
			var block = document.createElement('div');
			t = main.appendChild(document.createElement('span'));
			fillTimeToNow(soonest.startTime, t);
			main.appendChild(document.createElement('br'));
			var chip = document.createElement('div');
			chip.style.backgroundColor = chipColor;
			chip.style.border = 'solid 1px ' + borderColor;
			chip.style.borderRadius = '2px'
			chip.id = 'chip';
			chip.setAttribute('title', getMsg('Open'));
			addListener(chip, 'click', clickChip);
			var title = document.createElement('div');
			title.className = 'title';
			title.innerHTML = soonest.title ||
					getMsg('No_Subject') || '\u2026';
			chip.appendChild(title);
			if (soonest.location) {
				var loc = document.createElement('div');
				loc.className = 'location';
				loc.appendChild(document.createTextNode(soonest.location));
				chip.appendChild(loc);
			}
			block.appendChild(chip);
			addListener(block, 'mouseover',
					colorChangeFunc(block, hoverColor));
			addListener(block, 'mouseout',
					colorChangeFunc(block, chipColor));
			main.appendChild(block);
			var spacer=document.createElement('div');
			spacer.className = 'spacer';
			main.appendChild(spacer);
			curEventId = soonest.id;
		} else {
			// no events found
			t = main.appendChild(document.createElement('span'));
			t.appendChild(document.createTextNode(getMsg('None')))
		}
		t.style.cursor = 'default';
		t.title = getMsg('Clickable_Tooltip');
		addListener(t, 'click', getNextEvents);
		gadgets.window.adjustHeight(document.body.scrollHeight);
	}

	/**
	 * The user has clicked on the body.
	 * Show the current event onscreen.
	 */
	function clickChip() {
		if(curEventId)
			google.calendar.showEvent(curEventId);
	}

	/**
	 * The container has replied with the preferences
	 * @param {Object} prefs The set of preferences.
	 */
	function getPrefsCallback(prefs) {
		// Store interesting user preferences.
		timeOffset = prefs.current_time_offset;
		// Ask the container for the next few events.
		getNextEvents();
		// Request that we be called back when the date changes
		// and then re-fetch the next events.
		google.calendar.subscribeToDataChange(getNextEvents);
		// Kick off the drawing process.
		redrawPeriodically();
		gadgets.window.getViewportDimensions();
		// added so that it validates correctly
	}

	// Start the initialization process.
	google.calendar.getPreferences(getPrefsCallback);

	/**
	 * Create a span element containing the specified
	 * string, with the specified ID.
	 * @param {string} text The HTML displayed in the span
	 * @param {string} id the DOM ID for the span
	 * @return {DOM node} The span element
	 */
	function spanNode(text, id) {
		var node=document.createElement('span');
		node.id=id;
		node.appendChild(document.createTextNode(text));
		return node;
	}
}());